package com.xms.sdk.client;

import com.xms.core.data.Entity;
import com.xms.core.enums.AccessRightValue;
import com.xms.core.enums.OperationStage;
import com.xms.core.enums.OperationType;
import com.xms.core.exception.XmsException;
import com.xms.plugin.service.IEntityPluginExecutor;
import com.xms.schema.core.EntityMask;
import com.xms.schema.entity.Attribute;
import com.xms.schema.service.attribute.IAttributeFinderService;
import com.xms.sdk.core.OwnerObject;
import com.xms.sdk.core.RetrieverParameter;
import com.xms.sdk.core.query.ConditionOperator;
import com.xms.sdk.core.query.QueryExpression;
import com.xms.sdk.data.IOrganizationDataProvider;
import com.xms.sdk.data.IOrganizationDataRetriever;
import com.xms.sdk.event.EntityUpdatedEvent;
import com.xms.sdk.event.EntityUpdatingEvent;
import com.xms.sdk.extensions.IQueryMetadataFinder;
import com.xms.sdk.service.IDataAssigner;
import com.xms.sdk.service.IDataUpdater;
import com.xms.sdk.service.IEntityValidator;
import com.xms.utils.RegexUtil;
import com.xms.utils.UUIDUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * DataUpdater
 * </p>
 *
 * @author migo
 * @since 2021/9/6
 */
@Service
public class DataUpdater extends DataProviderBase implements IDataUpdater {
    private IOrganizationDataProvider _organizationDataProvider;
    private IOrganizationDataRetriever _organizationDataRetriever;
    private IAttributeFinderService _attributeFinderService;
    private IDataAssigner _dataAssigner;
    private IEntityValidator _entityValidator;
    private IEntityPluginExecutor _entityPluginExecutor;
    private IQueryMetadataFinder _queryMetadataFinder;

    @Autowired
    public void DataUpdater(IOrganizationDataProvider organizationDataProvider
            , IOrganizationDataRetriever organizationDataRetriever
            , IAttributeFinderService attributeFinderService
            , IDataAssigner dataAssigner
            , IEntityValidator entityValidator
            , IEntityPluginExecutor entityPluginExecutor
            , IQueryMetadataFinder queryMetadataFinder) {
        _organizationDataProvider = organizationDataProvider;
        _organizationDataRetriever = organizationDataRetriever;
        _attributeFinderService = attributeFinderService;
        _dataAssigner = dataAssigner;
        _entityValidator = entityValidator;
        _entityPluginExecutor = entityPluginExecutor;
        _queryMetadataFinder = queryMetadataFinder;
    }

    @Override
    public boolean update(Entity entity, boolean ignorePermissions) throws Exception {
        com.xms.schema.entity.Entity entityMetadata = getEntityMetaData(entity.Name);
        List<Attribute> attributeMetadatas = _attributeFinderService.getByEntityId(entityMetadata.getEntityid());
        verifyUpdate(entity, entityMetadata, attributeMetadatas);
        QueryExpression query = new QueryExpression(entity.Name);
        query.ColumnSet.AllColumns = true;
        query.Criteria.addCondition(entity.getIdName(), ConditionOperator.Equal, entity.getIdValue());
        Entity originalEntity = _organizationDataRetriever.getOne(RetrieverParameter.build(query, _currentUser).setIgnorePermissions(true)).data;
        //验证更新权限
        if (!ignorePermissions) {
            verifyEntityPermission(originalEntity, AccessRightValue.Update, entityMetadata);
        }
        OwnerObject ownerObj = entityMetadata.getEntitymask() == EntityMask.User.value() ? (OwnerObject) entity.getValue("ownerid") : null;
        boolean ownerChanged = ownerObj != null && !ownerObj.OwnerId.equals(originalEntity.getUUIDValue("ownerid"));//是否改变了所有者
        if (ownerChanged) {
            entity.remove("ownerid");
        }
        boolean result = true;
        //保存前发布事件
        publishEvents(originalEntity, entity, OperationStage.PreOperation, entityMetadata, attributeMetadatas);
        try {
            internalOnUpdate(originalEntity, entity, OperationStage.PreOperation, entityMetadata, attributeMetadatas);
            result = _organizationDataProvider.update(entity);
            if (result) {
                if (ownerChanged)//改变了所有者
                {
                    _dataAssigner.assign(entityMetadata, entity, ownerObj);
                }
                internalOnUpdate(originalEntity, entity, OperationStage.PostOperation, entityMetadata, attributeMetadatas);
            }
        } catch (Exception e) {
            onException(e);
        }
        if (result) {
            //保存成功后发布事件
            publishEvents(originalEntity, entity, OperationStage.PostOperation, entityMetadata, attributeMetadatas);
        }
        return result;
    }

    @Override
    public boolean update(Entity entity, QueryExpression query, boolean ignorePermissions) throws XmsException, SQLException {
        com.xms.schema.entity.Entity entityMetadata = getEntityMetaData(entity.Name);
        RetrieverParameter parameters = RetrieverParameter.build(query, _currentUser).setIgnorePermissions(ignorePermissions);
        if (!ignorePermissions) {
            parameters.setEntityPermissions(getUserEntityPermissions(query, AccessRightValue.Read));
            verifyEntityPermission(entity, AccessRightValue.Update, entityMetadata);
        }
        query.ColumnSet.Columns.clear();
        query.ColumnSet.Columns.add(entity.getIdName());

        return _organizationDataProvider.update(entity, parameters, _queryMetadataFinder.getAll(query));
    }

    @Override
    public boolean update(List<Entity> entities, boolean ignorePermissions) throws XmsException, SQLException {
        if (entities.isEmpty()) {
            onException("没有提供更新的数据");
        }
        String entityName = entities.get(0).Name;
        com.xms.schema.entity.Entity entityMetadata = getEntityMetaData(entityName);
        List<Attribute> attributeMetadatas = _attributeFinderService.getByEntityId(entityMetadata.getEntityid());
        for (Entity entity : entities) {
            verifyUpdate(entity, entityMetadata, attributeMetadatas);
        }
        String idName = entities.get(0).getIdName();
        QueryExpression query = new QueryExpression(entityName);
        query.ColumnSet.AllColumns = true;
        query.Criteria.addCondition(idName, ConditionOperator.In, entities.stream().map(x -> x.getIdValue()).collect(Collectors.toList()));
        List<Entity> existsEntities = _organizationDataRetriever.list(RetrieverParameter.build(query, _currentUser).setIgnorePermissions(ignorePermissions)).data;
        if (!ignorePermissions) {
            for (Entity entity : existsEntities) {
                verifyEntityPermission(entity, AccessRightValue.Update, entityMetadata);
            }
        }
        for (Entity entity : existsEntities) {
            Entity originalEntity = existsEntities.stream().filter(x -> x.getIdValue().equals(entity.getIdValue())).findFirst().get();
            //保存前发布事件
            publishEvents(originalEntity, entity, OperationStage.PreOperation, entityMetadata, attributeMetadatas);
        }
        boolean result = true;
        try {
            for (Entity entity : entities) {
                Entity originalEntity = existsEntities.stream().filter(x -> x.getIdValue().equals(entity.getIdValue())).findFirst().get();
                OwnerObject ownerObj = entityMetadata.getEntitymask() == EntityMask.User.value() ? (OwnerObject) entity.getValue("ownerid") : null;
                boolean ownerChanged = ownerObj != null && !ownerObj.OwnerId.equals(originalEntity.getUUIDValue("ownerid"));//是否改变了所有者
                if (ownerChanged) {
                    entity.remove("ownerid");
                }
                internalOnUpdate(originalEntity, entity, OperationStage.PreOperation, entityMetadata, attributeMetadatas);
                result = _organizationDataProvider.update(entity);
                if (ownerChanged)//改变了所有者
                {
                    _dataAssigner.assign(entityMetadata, entity, ownerObj);
                }
                internalOnUpdate(originalEntity, entity, OperationStage.PostOperation, entityMetadata, attributeMetadatas);
            }
        } catch (Exception e) {
            onException(e);
        }
        if (result) {
            for (Entity entity : existsEntities) {
                Entity originalEntity = existsEntities.stream().filter(x -> x.getIdValue().equals(entity.getIdValue())).findFirst().get();
                //保存后发布事件
                publishEvents(originalEntity, entity, OperationStage.PostOperation, entityMetadata, attributeMetadatas);
            }
        }
        return result;
    }

    @Override
    public boolean update(Entity entity) throws Exception {
        return this.update(entity, false);
    }

    @Override
    public boolean update(Entity entity, QueryExpression query) throws XmsException, SQLException {
        return this.update(entity, query, false);
    }

    @Override
    public boolean update(List<Entity> entities) throws XmsException, SQLException {
        return this.update(entities, false);
    }

    private void verifyUpdate(Entity entity, com.xms.schema.entity.Entity entityMetadata, List<Attribute> attributeMetadatas) throws XmsException {
        if (!entity.containsKey(entity.getIdName())
                || !RegexUtil.isUUID(entity.getStringValue(entity.getIdName())) || entity.getUUIDValue(entity.getIdName()).equals(UUIDUtil.Empty)) {
            onException("没有指定主键值");
        }
        _entityValidator.verifyValues(entity, entityMetadata, attributeMetadatas);
        if (attributeMetadatas.stream().anyMatch(x -> x.getName().equalsIgnoreCase("modifiedon"))) {
            entity.addIfNotContain("modifiedon", LocalDateTime.now());
        }
        if (attributeMetadatas.stream().anyMatch(x -> x.getName().equalsIgnoreCase("modifiedby"))) {
            entity.addIfNotContain("modifiedby", this._currentUser.getUser().SystemUserId);
        }
    }

    private void internalOnUpdate(Entity originData, Entity newData, OperationStage stage, com.xms.schema.entity.Entity entityMetadata, List<Attribute> attributeMetadatas) throws XmsException {
        try {
            _entityPluginExecutor.execute(OperationType.Update, stage, newData, entityMetadata, attributeMetadatas);
        } catch (Exception e) {
            onException(e);
        }
    }

    // 发布事件
    private void publishEvents(Entity originData, Entity newData, OperationStage stage, com.xms.schema.entity.Entity entityMetadata, List<Attribute> attributeMetadatas) {
        if (stage == OperationStage.PreOperation) {
            _eventPublisher.publish(new EntityUpdatingEvent(originData, newData, entityMetadata, attributeMetadatas));
        } else if (stage == OperationStage.PostOperation) {
            _eventPublisher.publish(new EntityUpdatedEvent(originData, newData, entityMetadata, attributeMetadatas));
        }
    }
}
